﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetNuke.Entities.Users;
using System.Xml.Linq;
using DotNetNuke.Security.Membership;
using BrandonHaynes.Membership.Factors.PromptControls;
using System.Web.UI;
using System.Web;
using System.Globalization;

namespace BrandonHaynes.Membership.Factors
	{
	/// <summary>
	/// A factor requiring a client certificate to authenticate (by default against a user's username)
	/// </summary>
	public class ClientCertificateFactor : IAuthenticationFactor
		{
		private IDictionary<string, string> Attributes { get; set; }

		public ClientCertificateFactor(IDictionary<string, string> attributes)
			{ Attributes = attributes; }

		#region IAuthenticationFactor Members

		public string Name { get { return "ClientCertificate"; } }

		public void Authenticate(UserInfo user, Credential credential)
			{
			var certificate = HttpContext.Current.Request.ClientCertificate;

			// No certificate implies that the user must present a certificate before authenticating
			if (certificate == null || !certificate.IsPresent)
				{
				credential.IncompleteFactors.Add(this);
				credential.Status = UserLoginStatus.LOGIN_SUCCESS;
				}
			// A valid certificate, but one that otherwise does not match the profileProperty used (by default
			// the user's username) is a failure.
			else if (certificate.IsValid || certificate.Subject != user.GetUserProperty(Attributes.GetValueOrDefault("profileProperty", "username")))
				credential.Status = UserLoginStatus.LOGIN_FAILURE;
			// Certificate exists, is valid, and matches the user.  Authenticate.
			else
				credential.Status = UserLoginStatus.LOGIN_SUCCESS;
			}

		/// <summary>
		/// We can't really prompt for a client certificate, but we can display a message indicating that one needs
		/// to be presented.
		/// </summary>
		public Control PromptControl
			{ get { return new MessagePrompt(Attributes); } }

		#endregion
		}
	}
